<?php
/**
 * Miffos framework
 * ----------------------------------------------
 *
 * Copyright (c) 2008, 2009 Martin Sadový (http://sodae.cz)
 *
 * This source file is subject to the "Miffos license" that is bundled
 * with this package in the file license.txt.
 *
 * @copyright  Copyright (c) 2008, 2009 Martin Sadový
 * @license    license.txt
 * @link       http://miffos.sodae.cz
 * @package    miffos
 * @version    $Id$
 */


require_once dirname(__FILE__).'/Html.php';
require_once dirname(__FILE__).'/Form/Render.php';
require_once dirname(__FILE__).'/Form/Control.php';

class Form implements ArrayAccess,IteratorAggregate{

    private $html;


    private $container = array();


    private $render;


    private $name;


    private $method;


    private $submit;


    private $parseDataOk = false;


    private $data = array();


    private $formError = array();

    private $autoRenderError = true;

    private static $formId = 0;
    
    const
        EQUAL   = 'equal',
        FILLED  = 'filled',
        NUMERIC = 'numeric',
        MINLENGTH  = 'minlength',
        MAXLENGTH  = 'maxlength',
        RANGE   = 'range',
        REGEXP  = 'preg',
        URL     = 'url',
        EMAIL   = 'email';


    public function __construct($path='',$method='post')
    {
      static $formId = 0;
      $form = Html::el('form');
      $form->name   = $this->name = 'form'.(self::$formId==0?'':self::$formId);
      $form->action = $path;
      $form->method = $this->method = $method;
      $this->html   = $form;
      self::$formId++;

    }



    public function getName()
    {
      return $this->name;
    }

    /**
    * Create Html element input -> text
    * @param string $name
    * @param string $label
    * @return object
    */
    public function addText($name,$label=null)
    {
      $this[$name] = new FormTextInput($this,$name,$label);
      return $this[$name];
    }



    /**
    * Create Html element input -> text
    * @param string $name
    * @param string $label
    * @return object
    */
    public function addTextarea($name,$label=null)
    {
      $this[$name] = new FormTextarea($this,$name,$label);
      return $this[$name];
    }



    /**
    * Create Html element input -> password
    * @param string $name
    * @param string $label
    * @return object
    */
    public function addPassword($name,$label=null)
    {
      $this[$name] = new FormPasswordInput($this,$name,$label);
      return $this[$name];
    }



    /**
    * Create Html element input -> submit
    * @param string $label
    * @return object
    */
    public function addSubmit($label=null,$name='submit')
    {
      $this[$name] = new FormSubmitButton($this,$name,$label);
      return $this[$name];
    }



    /**
    * Create Html element input -> reset
    * @param string $label
    * @return object
    */
    public function addReset($label=null,$name='reset')
    {
      $this[$name] = new FormResetButton($this,$name,$label);
      return $this[$name];
    }



    /**
    * Create Html element input -> checkbox
    * @param string $label
    * @return object
    */
    public function addCheckbox($name,$label,$check=false)
    {
      $this[$name] = new FormCheckboxInput($this,$name,$label,$check);
      return $this[$name];
    }



    /**
    * Create Html element input -> file
    * @param string $label
    * @return object
    */
    public function addFile($name,$label=null)
    {
      $this->html->enctype = "multipart/form-data";
      $this[$name] = new FormFileInput($this,$name,$label);
      return $this[$name];
    }



    /**
    * Create Html element input -> radio
    * @param string $label
    * @return object
    */
    public function addRadio($name,$label,$array)
    {
      $this[$name] = new FormRadioInput($this,$name,$label,$array);
      return $this[$name];
    }



    /**
    * Create Html element select
    * @param string $label
    * @return object
    */
    public function addSelect($name,$label,$array,$selected=null)
    {
      $this[$name] = new FormSelect($this,$name,$label,$array,$selected);
      return $this[$name];
    }


    /**
    * Create Free space
    * @param string $label
    * @return object
    */
    public function addFreeSpace($name,$label='')
    {
      $this[$name] = new FormFreeSpace($this,$name,$label);
      return $this[$name];
    }


    /**
    * Get html instance
    * @return object
    */
    public function getPrototype()
    {
      return $this->html;
    }



    /**
    * Render Form
    * @param bool $instance
    * @return string or object if param instance is true
    */
    public function render($instance = false)
    {
      if(!($this->render instanceof FormRender)){
         $this->render = new FormRender($this);
      }
      return $instance?$this->render:$this->render->render();
    }



    /**
    * Get data name=>value
    * @return array
    */
    public function getData()
    {
      return $this->data;
    }



    /**
    * Is submit ?
    * @return bool
    */
    public function isSubmit()
    {
      $this->_getDataFrom();
      return $this->submit;
    }



    /**
    * Is Valide ?
    * @return bool
    */
    public function isValid()
    {
      foreach($this->container as $k=>$control){
          if(!$control->isValid()){
             $false = true;
          }
      }
      return $false?false:true;
    }



    /**
     * Set or return auto render error
     * @param bool $render
     */
    public function autoRenderError($render=null){
        if(is_null($render)){
            return $this->autoRenderError;
        }
        $this->autoRenderError = $render;
    }


    /**
    * Call method | function after send
    * @return bool
    */
    public function afterSubmit($function,$object=null)
    {
      if($this->isSubmit()){
          if(is_null($object)){
              return call_user_func($function, $this->getData());
          }else{
              return call_user_method($function, $object, $this->getData());
          }
      }
    }



    /**
    * Parse data from $_GET or $_POST
    * @param string $from
    */
    private function _getDataFrom()
    {
      if($this->parseDataOk) return '';
      $input = ($this->method == 'get')?$_GET:$_POST;
      foreach($this->container as $name=>$container){
          if($container instanceof FormFileInput AND isset($_FILES[$this->name]['name'][$name])){
             $this->data[$name] = new FormUploadHelper($container,$_FILES[$this->name]);
          }elseif(isset($input[$this->getName()][$name])){
              if($container instanceof FormSubmitButton){
                  $this->submit = true;
              }else{
                  $container->setValue($input[$this->getName()][$name]);
                  $this->data[$name] = $container->getValue();
              }
          }
      }
      $this->parseDataOk = true;
    }



    /**
    * Add Message before render form
    * @param string $message
    */
    public function addMessage($message)
    {
      $this->formError[] = $message;
    }



    /**
    * Return all message
    * @return array
    */
    public function getMessages()
    {
      return $this->formError;
    }



    /**
    * Return Html instance for message
    * @return object
    */
    public function getMessagePrototype()
    {
      if(!($this->errorHtml instanceof Html)){
          $this->errorHtml = Html::el('div');
      }
      return $this->errorHtml;
    }



    /**
    * Magic function :)
    */
    public function __toString()
    {
      return $this->render();
    }

    
    /**
    * ArrayAccess interface
    * @return object|null
    */
    public function offsetSet($offset, $value)
    {
        if(isset($this[$offset])) throw new Exception("Form with name '".$offset."' existed !");
        $this->container[$offset] = $value;
    }


    public function offsetExists($offset)
    {
        return isset($this->container[$offset]);
    }


    public function offsetUnset($offset)
    {
        unset($this->container[$offset]);
        unset($this->data[$offset]);
    }

    
    public function offsetGet($offset) {
        return isset($this->container[$offset])?$this->container[$offset]:null;
    }

    /**
    * ArrayIterator interface
    * @return  ArrayIterator
    */
    public function getIterator()
    {
      return new ArrayIterator($this->container);
    }
}

?>